#include "gpio_ctrl.h"
#include "minmea.h"
#include "uart_interface.h"
#include <pthread.h>
#include <sys/time.h>
#include <termios.h>

static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

#define PROGRAM_NAME "timing_interface"
#define INDENT_SPACES "    "

#define TIMING_MODULE_PWR_PORT "gpiochip10"
#define TIMING_MODULE_PWR_PIN 0
#define ENABLE 1
#define DISABLE 0

static int fd;

static void parse_minmea(char const *line) {
    switch (minmea_sentence_id(line, false)) {
    case MINMEA_SENTENCE_RMC: {
        struct minmea_sentence_rmc frame;
        if (minmea_parse_rmc(&frame, line)) {
            printf(INDENT_SPACES "$xxRMC: raw coordinates and speed: (%d/%d,%d/%d) %d/%d\n",
                   frame.latitude.value, frame.latitude.scale, frame.longitude.value,
                   frame.longitude.scale, frame.speed.value, frame.speed.scale);
            printf(INDENT_SPACES "$xxRMC fixed-point coordinates and speed scaled to three decimal "
                                 "places: (%d,%d) %d\n",
                   minmea_rescale(&frame.latitude, 1000), minmea_rescale(&frame.longitude, 1000),
                   minmea_rescale(&frame.speed, 1000));
            printf(INDENT_SPACES "$xxRMC floating point degree coordinates and speed: (%f,%f) %f\n",
                   minmea_tocoord(&frame.latitude), minmea_tocoord(&frame.longitude),
                   minmea_tofloat(&frame.speed));
        } else {
            printf(INDENT_SPACES "$xxRMC sentence is not parsed\n");
        }
    } break;

    case MINMEA_SENTENCE_GGA: {
        struct minmea_sentence_gga frame;
        if (minmea_parse_gga(&frame, line)) {
            printf(INDENT_SPACES "$xxGGA: fix quality: %d\n", frame.fix_quality);
        } else {
            printf(INDENT_SPACES "$xxGGA sentence is not parsed\n");
        }
    } break;

    case MINMEA_SENTENCE_GST: {
        struct minmea_sentence_gst frame;
        if (minmea_parse_gst(&frame, line)) {
            printf(INDENT_SPACES "$xxGST: raw latitude,longitude and altitude error deviation: "
                                 "(%d/%d,%d/%d,%d/%d)\n",
                   frame.latitude_error_deviation.value, frame.latitude_error_deviation.scale,
                   frame.longitude_error_deviation.value, frame.longitude_error_deviation.scale,
                   frame.altitude_error_deviation.value, frame.altitude_error_deviation.scale);
            printf(INDENT_SPACES
                   "$xxGST fixed point latitude,longitude and altitude error deviation"
                   " scaled to one decimal place: (%d,%d,%d)\n",
                   minmea_rescale(&frame.latitude_error_deviation, 10),
                   minmea_rescale(&frame.longitude_error_deviation, 10),
                   minmea_rescale(&frame.altitude_error_deviation, 10));
            printf(INDENT_SPACES "$xxGST floating point degree latitude, longitude and altitude "
                                 "error deviation: (%f,%f,%f)",
                   minmea_tofloat(&frame.latitude_error_deviation),
                   minmea_tofloat(&frame.longitude_error_deviation),
                   minmea_tofloat(&frame.altitude_error_deviation));
        } else {
            printf(INDENT_SPACES "$xxGST sentence is not parsed\n");
        }
    } break;

    case MINMEA_SENTENCE_GSV: {
        struct minmea_sentence_gsv frame;
        if (minmea_parse_gsv(&frame, line)) {
            printf(INDENT_SPACES "$xxGSV: message %d of %d\n", frame.msg_nr, frame.total_msgs);
            printf(INDENT_SPACES "$xxGSV: satellites in view: %d\n", frame.total_sats);
            for (int i = 0; i < 4; i++)
                printf(INDENT_SPACES "$xxGSV: sat nr %d, elevation: %d, azimuth: %d, snr: %d dbm\n",
                       frame.sats[i].nr, frame.sats[i].elevation, frame.sats[i].azimuth,
                       frame.sats[i].snr);
        } else {
            printf(INDENT_SPACES "$xxGSV sentence is not parsed\n");
        }
    } break;

    case MINMEA_SENTENCE_VTG: {
        struct minmea_sentence_vtg frame;
        if (minmea_parse_vtg(&frame, line)) {
            printf(INDENT_SPACES "$xxVTG: true track degrees = %f\n",
                   minmea_tofloat(&frame.true_track_degrees));
            printf(INDENT_SPACES "        magnetic track degrees = %f\n",
                   minmea_tofloat(&frame.magnetic_track_degrees));
            printf(INDENT_SPACES "        speed knots = %f\n", minmea_tofloat(&frame.speed_knots));
            printf(INDENT_SPACES "        speed kph = %f\n", minmea_tofloat(&frame.speed_kph));
        } else {
            printf(INDENT_SPACES "$xxVTG sentence is not parsed\n");
        }
    } break;

    case MINMEA_SENTENCE_ZDA: {
        struct minmea_sentence_zda frame;
        if (minmea_parse_zda(&frame, line)) {
            printf(INDENT_SPACES "$xxZDA: %d:%d:%d %02d.%02d.%d UTC%+03d:%02d\n", frame.time.hours,
                   frame.time.minutes, frame.time.seconds, frame.date.day, frame.date.month,
                   frame.date.year, frame.hour_offset, frame.minute_offset);
        } else {
            printf(INDENT_SPACES "$xxZDA sentence is not parsed\n");
        }
    } break;

    case MINMEA_INVALID: {
        printf(INDENT_SPACES "$xxxxx sentence is not valid\n");
    } break;

    default: {
        printf(INDENT_SPACES "$xxxxx sentence is not parsed\n");
    } break;
    }
}

int timing_module_init() {
    int ret;

    gpio_set_value(TIMING_MODULE_PWR_PORT, TIMING_MODULE_PWR_PIN, ENABLE);

    /* 打开所有数传模块电源，临时测试 */
    gpio_set_value("gpiochip9", 2, ENABLE);
    gpio_set_value("gpiochip9", 3, ENABLE);
    gpio_set_value("gpiochip9", 4, ENABLE);

    fd = uart_open(fd, "/dev/ttySTM2");
    if (fd == -1)
        errmsg_die("uart_open error\n");

    if (ret = uart_set(fd, 115200, 0, 8, 'N', 1) == -1)
        errmsg_die("uart set failed!\n");
    return ret;
}

struct tm time_sync() {
    pthread_mutex_lock(&lock);
    char ch;
    int ret;
    char line[MINMEA_MAX_SENTENCE_LENGTH];
    int i = 0;
    struct tm the_tm;

    tcflush(fd, TCIFLUSH);
    while (ret = uart_read(fd, &ch, 1) != -1) {
        if (ch == '$') {
            line[0] = ch;
            i = 1;
        } else if (i > 0) {
            line[i++] = ch;
            if (ch != '\n') {
                if (i >= MINMEA_MAX_SENTENCE_LENGTH)
                    i = 0;
            } else {
                line[i] = '\0';
                if (minmea_sentence_id(line, false) == MINMEA_SENTENCE_ZDA) {
                    struct minmea_sentence_zda frame;
                    if (minmea_parse_zda(&frame, line)) {
                        printf(INDENT_SPACES "$xxZDA: %d:%d:%d %02d.%02d.%d UTC%+03d:%02d\n",
                               frame.time.hours, frame.time.minutes, frame.time.seconds,
                               frame.date.day, frame.date.month, frame.date.year, frame.hour_offset,
                               frame.minute_offset);

                        minmea_getdatetime(&the_tm, &frame.date, &frame.time);
                        break;
                    }
                }
                i = 0;
            }
        }
    }
    pthread_mutex_unlock(&lock);
    return the_tm;
}

void read_gps(int32_t *latitude, int32_t *longitude, int32_t *altitude) {
    pthread_mutex_lock(&lock);
    char ch;
    int ret;
    char line[MINMEA_MAX_SENTENCE_LENGTH];
    int i = 0;
    struct tm the_tm;

    tcflush(fd, TCIFLUSH);
    while (ret = uart_read(fd, &ch, 1) != -1) {
        if (ch == '$') {
            line[0] = ch;
            i = 1;
        } else if (i > 0) {
            line[i++] = ch;
            if (ch != '\n') {
                if (i >= MINMEA_MAX_SENTENCE_LENGTH)
                    i = 0;
            } else {
                line[i] = '\0';
                if (minmea_sentence_id(line, false) == MINMEA_SENTENCE_GGA) {
                    struct minmea_sentence_gga frame;
                    if (minmea_parse_gga(&frame, line)) {
                        *latitude = frame.latitude.value;
                        *longitude = frame.longitude.value;
                        *altitude = minmea_rescale(&frame.altitude, 1000);
                        break;
                    }
                }
                i = 0;
            }
        }
    }
    pthread_mutex_unlock(&lock);
}

void timing_module_task() {
    char ch;
    int ret;
    char line[MINMEA_MAX_SENTENCE_LENGTH];
    int i = 0;

    while (ret = uart_read(fd, &ch, 1) != -1) {
        printf("%c", ch);
        if (ch == '$') {
            line[0] = ch;
            i = 1;
        } else if (i > 0) {
            line[i++] = ch;
            if (ch != '\n') {
                if (i >= MINMEA_MAX_SENTENCE_LENGTH)
                    i = 0;
            } else {
                line[i] = '\0';
                parse_minmea(line);
                i = 0;
            }
        }
    }
    if (ret == -1)
        errmsg_die("uart read failed!\n");
}

int close_timing_module() {
    int ret;
    ret = uart_close(fd);
    if (ret == -1)
        errmsg_die("uart_close error\n");
    gpio_set_value(TIMING_MODULE_PWR_PORT, TIMING_MODULE_PWR_PIN, DISABLE);
    return ret;
}
